#ifndef _CU_COLOR_H_
#define _CU_COLOR_H_

template<class T>
SDH_CALL float3 RGB_to_HSV(T rgb)
{
	float r, g, b, delta;
	float colorMax, colorMin;
	float h=0.0f, s=0.0f, v=0.0f;
	float3 hsv= f3();

	r = rgb.x;
	g = rgb.y;
	b = rgb.z;

	colorMax = max(max (r, g), b);
	colorMin = min(min (r, g), b);
	
	v = colorMax; // this is value
	
	s = (colorMax != 0) ? (colorMax - colorMin) / colorMax : s;

	if (s != 0) // if not achromatic
	{
		delta = colorMax - colorMin;

		if (r == colorMax)
		{
			h = (g-b)/delta;
		}
		
		if (g == colorMax)
		{
			h = 2.0 + (b-r) / delta;
		}
		
		if (b == colorMax)
		{
			h = 4.0 + (r-g)/delta;
		}

		h *= 60;

		h = (h < 0) ? h+=360 : h;

		hsv.x = h / 360.0; // moving h to be between 0 and 1.

	}
	
	hsv.y = s;
	hsv.z = v;

	return hsv;
}

template <class T>
SDH_CALL float3 HSV_to_RGB(T hsv)
{
	float3 color = f3();
	
	float f,p,q,t;
	
	float h,s,v;
	
	float r=0.0f;
	float g=0.0f; 
	float b=0.0f;

	float i;

	if (hsv.y == 0)
	{
		if (hsv.z != 0)
		{
			color = f4(hsv.z);
		}
	}
	else
	{
		h = hsv.x * 360.0f;
		s = hsv.y;
		v = hsv.z;
		h = (h == 360.0f) ? 0.0f : h;

		h /=60;
		
		i = floor (h);
		f = h-i;
		p = v * (1.0f - s);

		q = v * (1.0f - (s * f));
		t = v * (1.0f - (s * (1.0f -f)));

		if (i == 0)
		{
			r = v;
			g = t;
			b = p;
		}
		
		if (i == 1)
		{
			r = q;
			g = v;
			b = p;
		}
		
		if (i == 2)
		{
			r = p;
			g = v;
			b = t;
		}
		
		if (i == 3)
		{
			r = p;
			g = q;
			b = v;
		}
		
		if (i == 4)
		{
			r = t;
			g = p;
			b = v;
		}
		
		if (i == 5)
		{
			r = v;
			g = p;
			b = q;
		}

		color.x = r;
		color.y = g;
		color.z = b;
	}

	return color;
}

template<class T>
SDH_CALL float3 RGB_to_YUV(T rgb)
{
	float3 yuv = f3();

	yuv.x =  0.299f   * rgb.x + 0.587f   * rgb.y + 0.114f   * rgb.z;
	yuv.y = -0.14713f * rgb.x - 0.28887f * rgb.y + 0.436f   * rgb.z;
	yuv.z =  0.615f   * rgb.x - 0.51499f * rgb.y - 0.10001f * rgb.z;

	// u component is within [-0.436, 0.436] interval
	// u is mapped to [0.0f, 1.0f]
	yuv.y = (yuv.y / 0.436f + 1.0f) * 0.5f; 
	
	// v component is within [-0.615, 0.615] interval
	// v is mapped to [0.0f, 1.0f]
	yuv.z = (yuv.z / 0.615f + 1.0f) * 0.5f;
	
	return yuv;
}

template<class T>
SDH_CALL float3 YUV_to_RGB(T yuv)
{
	float3 rgb = f3();

	// u is unmapped from [0.0f, 1.0f] to [-0.436, 0.436] interval
	yuv.y = (yuv.y * 2.0f - 1.0f) * 0.436f;
	// u is unmapped from [0.0f, 1.0f] to [-0.615, 0.615] interval
	yuv.z = (yuv.z * 2.0f - 1.0f) * 0.615f;

	rgb.x = yuv.x + 1.13983f * yuv.z;
	rgb.y = yuv.x - 0.39465f * yuv.y - 0.58060 * yuv.z;
	rgb.z = yuv.x + 2.03211f * yuv.y;

	return rgb;
}

template<class T>
SDH_CALL float3 RGB_to_YCbCr(T rgb)
{
	const float Kb = 0.114f;
	const float Kr = 0.299f;
	const float Kg = 1.0f - Kb - Kr;

	float3 ycbcr = f3();

	ycbcr.x = Kr * rgb.x + Kg * rgb.y + Kb * rgb.z;
  ycbcr.y = 0.5f * (rgb.z - ycbcr.x) / (1 - Kb);
  ycbcr.z = 0.5f * (rgb.x - ycbcr.x) / (1 - Kr);

	// map to [0, 1]
	ycbcr.y += 0.5f; 
	ycbcr.z += 0.5f; 

	return ycbcr;
}

template<class T>
SDH_CALL float3 YCbCr_to_RGB(T ycbcr)
{
	const float Kb = 0.114f;
	const float Kr = 0.299f;
	const float Kg = 1.0f - Kb - Kr;

	float3 rgb = f3();

	// unmap to [-0.5, 0.5]
	ycbcr.y -= 0.5f;
	ycbcr.z -= 0.5f;

	rgb.z = 2.0f * ycbcr.y * (1 - Kb) + ycbcr.x;
	rgb.x = 2.0f * ycbcr.z * (1 - Kr) + ycbcr.x;

	rgb.y = (ycbcr.x - Kr * rgb.x - Kb * rgb.z) / Kg;
	
	return rgb;
}

#endif